package com.kexio.auth.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.kexio.auth.annotation.RequiresPermission;
import com.kexio.auth.dto.UserAuthInfo;
import com.kexio.auth.util.AuthContextUtils;
import com.kexio.common.exception.BusinessException;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;

/**
 * RequiresPermission权限验证切面
 * 
 * 职责分工：
 * - SaInterceptor (Sa-Token): 基础认证检查（登录状态验证）
 * - RequiresPermissionAspect: 细粒度权限验证（注解驱动的业务权限）
 * 
 * 与Sa-Token集成，使用Sa-Token的底层API实现高级权限注解功能：
 * - 多权限AND/OR逻辑
 * - 超级管理员跳过
 * - 权限验证级别（STRICT/LOOSE）
 * - 数据权限聚合
 * - 通配符权限匹配
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Aspect
@Component
@Order(150)  // 在Sa-Token基础认证之后(100)，数据权限之前(200)
public class RequiresPermissionAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(RequiresPermissionAspect.class);
    
    /**
     * 拦截所有标注@RequiresPermission的方法
     */
    @Around("@annotation(com.kexio.auth.annotation.RequiresPermission)")
    public Object handleRequiresPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RequiresPermission requiresPermission = method.getAnnotation(RequiresPermission.class);
        
        if (requiresPermission == null) {
            return joinPoint.proceed();
        }
        
        // 1. 检查是否已登录（基于Sa-Token）
        if (!StpUtil.isLogin()) {
            logger.warn("权限验证失败: 用户未登录, method={}", method.getName());
            throw new NotLoginException("用户未登录", StpUtil.getLoginType(), NotLoginException.NOT_TOKEN);
        }
        
        // 2. 获取当前用户信息
        UserAuthInfo userInfo = AuthContextUtils.getCurrentUser();
        if (userInfo == null) {
            logger.warn("权限验证失败: 无法获取用户信息, method={}", method.getName());
            throw new NotLoginException("无法获取用户信息", StpUtil.getLoginType(), NotLoginException.INVALID_TOKEN);
        }
        
        // 3. 检查超级管理员跳过
        boolean shouldIgnoreSuperAdmin = requiresPermission.ignoreSuperAdmin();
        boolean isUserSuperAdmin = isSuperAdmin(userInfo);
        logger.info("[超管检查] userId={}, ignoreSuperAdmin={}, isSuperAdmin={}, method={}", 
            userInfo.getUserId(), shouldIgnoreSuperAdmin, isUserSuperAdmin, method.getName());
        
        if (shouldIgnoreSuperAdmin && isUserSuperAdmin) {
            logger.info("[超管跳过] 超级管理员跳过权限验证: userId={}, method={}", 
                userInfo.getUserId(), method.getName());
            return joinPoint.proceed();
        }
        
        // 4. 执行权限验证
        String[] requiredPermissions = requiresPermission.value();
        boolean hasPermission = checkPermissions(
            requiredPermissions, 
            requiresPermission.logical(),
            requiresPermission.level(),
            userInfo
        );
        
        if (!hasPermission) {
            String errorMessage = requiresPermission.message();
            if (errorMessage.isEmpty()) {
                errorMessage = "权限不足: 需要权限 " + Arrays.toString(requiredPermissions);
            }
            
            // 日志也统一从Sa-Token获取最新权限，确保日志准确性
            List<String> actualPermissions = Collections.emptyList();
            try {
                actualPermissions = StpUtil.getPermissionList();
            } catch (Exception e) {
                logger.debug("获取权限列表失败: {}", e.getMessage());
            }
            
            logger.warn("权限验证失败: userId={}, requiredPermissions={}, userPermissions={}, method={}", 
                userInfo.getUserId(), Arrays.toString(requiredPermissions), 
                actualPermissions, method.getName());
            
            throw BusinessException.accessDenied(errorMessage);
        }
        
        // 5. 数据权限聚合处理
        if (requiresPermission.aggregateDataScope()) {
            aggregateDataScope(userInfo);
        }
        
        logger.debug("权限验证通过: userId={}, permissions={}, method={}", 
            userInfo.getUserId(), Arrays.toString(requiredPermissions), method.getName());
        
        return joinPoint.proceed();
    }
    
    /**
     * 检查权限
     * 
     * 统一使用Sa-Token权限缓存，避免双缓存不一致问题
     */
    private boolean checkPermissions(String[] requiredPermissions, 
                                   RequiresPermission.Logical logical,
                                   RequiresPermission.VerifyLevel level,
                                   UserAuthInfo userInfo) {
        
        if (requiredPermissions.length == 0) {
            return true;
        }
        
        // 修改：直接从Sa-Token获取最新权限，而不是从userInfo缓存读取
        List<String> userPermissions;
        try {
            userPermissions = StpUtil.getPermissionList();
            if (userPermissions == null || userPermissions.isEmpty()) {
                logger.debug("用户权限列表为空: userId={}", userInfo.getUserId());
                return false;
            }
        } catch (Exception e) {
            logger.error("获取用户权限失败: userId={}, error={}", userInfo.getUserId(), e.getMessage());
            return false;
        }
        
        // AND逻辑：需要拥有所有权限
        if (logical == RequiresPermission.Logical.AND) {
            return Arrays.stream(requiredPermissions)
                .allMatch(required -> hasPermission(required, userPermissions, level));
        }
        
        // OR逻辑：只需拥有任意一个权限
        return Arrays.stream(requiredPermissions)
            .anyMatch(required -> hasPermission(required, userPermissions, level));
    }
    
    /**
     * 检查单个权限
     */
    private boolean hasPermission(String requiredPermission, 
                                List<String> userPermissions, 
                                RequiresPermission.VerifyLevel level) {
        
        // STRICT模式：精确匹配
        if (level == RequiresPermission.VerifyLevel.STRICT) {
            return userPermissions.contains(requiredPermission);
        }
        
        // LOOSE模式：支持通配符匹配
        // 1. 精确匹配
        if (userPermissions.contains(requiredPermission)) {
            return true;
        }
        
        // 2. 通配符匹配：用户权限中的 * 匹配所需权限
        for (String userPerm : userPermissions) {
            if (userPerm.endsWith("*")) {
                String prefix = userPerm.substring(0, userPerm.length() - 1);
                if (requiredPermission.startsWith(prefix)) {
                    return true;
                }
            }
        }
        
        // 3. 层级匹配：检查上级权限
        // 例如：拥有 system:user 权限，可以访问 system:user:list
        String[] parts = requiredPermission.split(":");
        for (int i = parts.length - 1; i > 0; i--) {
            String parentPermission = String.join(":", Arrays.copyOf(parts, i));
            if (userPermissions.contains(parentPermission)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 检查是否为超级管理员
     * 
     * 统一使用Sa-Token角色缓存，避免从userInfo读取
     */
    private boolean isSuperAdmin(UserAuthInfo userInfo) {
        try {
            // 从Sa-Token获取最新角色列表
            List<String> roles = StpUtil.getRoleList();
            logger.info("[角色检查] userId={}, roles={}", userInfo.getUserId(), roles);
            
            if (roles == null || roles.isEmpty()) {
                logger.warn("[角色检查] 角色列表为空: userId={}", userInfo.getUserId());
                return false;
            }
            
            // 检查角色中是否包含超级管理员角色
            boolean isSuperAdmin = roles.stream()
                .anyMatch(roleCode -> 
                    "ROLE_SUPER_ADMIN".equals(roleCode) ||
                    "role_super_admin".equals(roleCode) ||
                    "super_admin".equals(roleCode) ||
                    "SUPER_ADMIN".equals(roleCode)
                );
            
            logger.info("[角色检查结果] userId={}, isSuperAdmin={}, roles={}", 
                userInfo.getUserId(), isSuperAdmin, roles);
            
            return isSuperAdmin;
        } catch (Exception e) {
            logger.error("[角色检查] 获取角色列表失败: userId={}, error={}", userInfo.getUserId(), e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 数据权限聚合处理
     */
    private void aggregateDataScope(UserAuthInfo userInfo) {
        // 预留扩展：可根据用户的部门信息自动设置数据权限上下文
        logger.debug("数据权限聚合处理: userId={}, deptId={}", 
            userInfo.getUserId(), userInfo.getDeptId());
    }
}
